//    entity language compiler Copyright (C) 2011 Vincent Belliard

//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation version 3 of the License.

//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.

//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <typeinfo>
#include "types.h"
#include "entity.h"
#include "method.h"
#include "type.h"
#include "expression.h"
#include "object_class.h"

// }{--------------------------------------------------------------

void T_type_void::dump(FILE *file) const
{
fprintf(file, "void") ;
}

void T_type_void::generate_type_name(FILE *file, int generate_mode) const
{
fprintf(file, "void") ;
}

// }{--------------------------------------------------------------

void T_type_null::dump(FILE *file) const
{
fprintf(file, "null") ;
}

void T_type_null::generate_type_name(FILE *file, int generate_mode) const
{
fprintf(file, "null") ;
}

// }{--------------------------------------------------------------

void T_type_identifier::dump(FILE *file) const
{
if (object_class != NULL) fprintf(file, "(%p)%s", object_class, object_class->name.string()) ;
else fprintf(file, "%s", identifier.string()) ;
}

void T_type_identifier::verify_semantic(T_entity_compiler *compiler, T_entity_program *program, T_scope *scope, T_type_method *method_type)
{
if (object_class == NULL)
	{
	object_class = program->search_class(identifier) ;
	if (object_class == NULL) compiler->add_error(location, "class %s not found.", identifier.string()) ;
	}
}

int T_type_identifier::verify_base_class_semantic(T_entity_compiler *compiler)
{
if (object_class->semantic_state == semantic_state_running) return FALSE ;
return object_class->verify_base_class_semantic(compiler) ;
}

int T_type_identifier::is_object_class(T_object_class *base_class) const
{
T_object_class *loc = object_class ;
for(;;)
	{
	if (loc == base_class) return TRUE ;
	if (loc->base_type == NULL) return FALSE ;
	if (loc->base_type->get_class() != class_type_identifier) return FALSE ;
	loc = ((T_type_identifier *)loc->base_type)->object_class ;
	}
}

int T_type_identifier::can_overload(const T_type *ref) const
{
if (ref->get_class() != class_type_identifier) return FALSE ;
//fprintf(stderr, "classes %s(%p) %s(%p)\n", object_class->name.string(), object_class, ((T_type_identifier *)ref)->object_class->name.string(), ((T_type_identifier *)ref)->object_class) ;
return object_class == ((T_type_identifier *)ref)->object_class ;
}

int T_type_identifier::is_sub_type(const T_type *ref) const
{
if (ref->get_class() == class_type_identifier)
	{
	if (object_class == ((T_type_identifier *)ref)->object_class) return TRUE ;
	}
if (object_class->base_type == NULL) return FALSE ;
return object_class->base_type->is_sub_type(ref) ;
}

int T_type_identifier::is_good_for_call(T_entity_program *program, const T_type *call_type) const
{
return (*object_class->is_good_for_call)(object_class, program, call_type) ;
}

const T_object_class_field *T_type_identifier::search_field(T_entity_program *program, int direct_search, const T_string name) const
{
return object_class->search_field(program, name) ;
}

const T_object_class_method *T_type_identifier::search_call(T_entity_program *program, int direct_search, const T_string name, T_expression_call_access *call) const
{
//fprintf(stderr, "class %s : search call %s\n", object_class->name.string(), name.string()) ;
return object_class->search_call(program, name, call) ;
}

const T_constructor *T_type_identifier::search_constructor(T_entity_program *program, T_expression_constructor_arguments *constructor)
{
return object_class->search_constructor(program, constructor) ;
}

void T_type_identifier::generate_class_name(FILE *file) const
{
fprintf(file, "CLASS_%s", object_class->name.string()) ;
}

void T_type_identifier::generate_type_name(FILE *file, int generate_mode) const
{
switch (generate_mode)
	{
	case type_generate_value: fprintf(file, "CLASS_%s", object_class->name.string()) ; break ;
	case type_generate_instance: fprintf(file, "INSTANCE_%s", object_class->name.string()) ; break ;
	case type_generate_introspect: fprintf(file, "INTROSPECT_%s", object_class->name.string()) ; break ;
	case type_generate_class_reference: fprintf(file, "CLASS_%s *", object_class->name.string()) ; break ;
	case type_generate_instance_reference: fprintf(file, "INSTANCE_%s *", object_class->name.string()) ; break ;
	case type_generate_introspect_reference: fprintf(file, "INTROSPECT_%s *", object_class->name.string()) ; break ;
	case type_generate_header_reference: fprintf(file, "HEADER_%s *", object_class->name.string()) ; break ;
	case type_generate_header: fprintf(file, "HEADER_%s", object_class->name.string()) ; break ;
	case type_generate_introspect_object: fprintf(file, "introspect_%s", object_class->name.string()) ; break ;
	}
}

void T_type_identifier::generate_argv(FILE *file, const T_string &name) const
{
(*object_class->generate_argv)(file, name) ;
}

void T_type_identifier::generate_type(FILE *file) const
{
fprintf(file, "new T_type_class(&introspect_%s)", object_class->name.string()) ;
}

void T_type_identifier::generate_virtuals(FILE *file, T_object_class *ref) const
{
object_class->generate_virtuals(file, ref) ;
}

// }{--------------------------------------------------------------
/*
void T_type_boolean::dump(FILE *file) const
{
fprintf(file, "boolean") ;
}

// }{--------------------------------------------------------------

void T_type_character::dump(FILE *file) const
{
fprintf(file, "character") ;
}

void T_type_character::generate_type_name(FILE *file, int generate_mode) const
{
fprintf(file, "character") ;
}

// }{--------------------------------------------------------------

void T_type_string::dump(FILE *file) const
{
fprintf(file, "string") ;
}

void T_type_string::generate_type_name(FILE *file, int generate_mode) const
{
fprintf(file, "T_string") ;
}
*/
// }{--------------------------------------------------------------
/*
void T_type_integer::dump(FILE *file) const
{
if (natural) fprintf(file, "natural") ;
switch (size)
	{
	case 8: fprintf(file, "integer_8") ; break ;
	case 16: fprintf(file, "integer_16") ; break ;
	case 32: fprintf(file, "integer_32") ; break ;
	case 64: fprintf(file, "integer_64") ; break ;
	}
}

void T_type_integer::generate_type_name(FILE *file, int generate_mode) const
{
if (natural)
	{
	switch (size)
		{
		case 8: fprintf(file, "uint_8") ; break ;
		case 16: fprintf(file, "uint_16") ; break ;
		case 32: fprintf(file, "uint_32") ; break ;
		case 64: fprintf(file, "uint_64") ; break ;
		}
	}
else
	{
	switch (size)
		{
		case 8: fprintf(file, "int_8") ; break ;
		case 16: fprintf(file, "int_16") ; break ;
		case 32: fprintf(file, "int_32") ; break ;
		case 64: fprintf(file, "int_64") ; break ;
		}
	}
}

// }{--------------------------------------------------------------

void T_type_list_link::dump(FILE *file) const
{
fprintf(file, "list_link") ;
}

void T_type_list_link::generate_type_name(FILE *file, int generate_mode) const
{
fprintf(file, "LINK<void>") ;
}
*/

// }{--------------------------------------------------------------

void T_type_list::dump(FILE *file) const
{
if (mode_weak) fprintf(file, "weak ") ;
fprintf(file, "list of") ;
type->dump(file) ;
if (*link_name.string() != 0) fprintf(file, " on %s", link_name.string()) ;
}

const T_object_class_field *T_type_list::search_field(T_entity_program *program, int direct_search, const T_string name) const
{
if (strcmp(name.string(), "first_iterator") == 0)
	{
	T_object_class_field *loc = new T_object_class_field(location, FALSE, "first_iterator") ;
	loc->type = new T_type_reference(location, FALSE, type) ;
	return loc ;
	}
if (strcmp(name.string(), "last_iterator") == 0)
	{
	T_object_class_field *loc = new T_object_class_field(location, FALSE, "last_iterator") ;
	loc->type = new T_type_reference(location, FALSE, type) ;
	return loc ;
	}
if (strcmp(name.string(), "count") == 0)
	{
	T_object_class_field *loc = new T_object_class_field(location, FALSE, "count") ;
	loc->type = new T_type_identifier(location, program->integer_32_class) ;
	return loc ;
	}
return NULL ;
}

const T_object_class_method *T_type_list::search_call(T_entity_program *program, int direct_search, const T_string name, T_expression_call_access *call) const
{
if (strcmp(name.string(), "insert") == 0)
	{
	T_object_class_method *loc = new T_object_class_method(NULL, location, FALSE, FALSE, FALSE, name) ;
	T_type_method *type_loc = new T_type_method(location, FALSE) ;
	type_loc->returned_type = new T_type_void(location) ;
	loc->type = type_loc ;
	return loc ;
	}
if (strcmp(name.string(), "insert_before") == 0)
	{
	T_object_class_method *loc = new T_object_class_method(NULL, location, FALSE, FALSE, FALSE, name) ;
	T_type_method *type_loc = new T_type_method(location, FALSE) ;
	type_loc->returned_type = new T_type_void(location) ;
	loc->type = type_loc ;
	return loc ;
	}
if (strcmp(name.string(), "insert_after") == 0)
	{
	T_object_class_method *loc = new T_object_class_method(NULL, location, FALSE, FALSE, FALSE, name) ;
	T_type_method *type_loc = new T_type_method(location, FALSE) ;
	type_loc->returned_type = new T_type_void(location) ;
	loc->type = type_loc ;
	return loc ;
	}
if (strcmp(name.string(), "remove") == 0)
	{
	T_object_class_method *loc = new T_object_class_method(NULL, location, FALSE, FALSE, FALSE, name) ;
	T_type_method *type_loc = new T_type_method(location, FALSE) ;
	type_loc->returned_type = new T_type_void(location) ;
	loc->type = type_loc ;
	return loc ;
	}
return NULL ;
}

void T_type_list::verify_semantic(T_entity_compiler *compiler, T_entity_program *program, T_scope *scope, T_type_method *method_type)
{
if (type != NULL) type->verify_semantic(compiler, program, scope, method_type) ;
}

void T_type_list::generate_type_name(FILE *file, int generate_mode) const
{
fprintf(file, "LIST<") ;
type->generate_type_name(file, type_generate_value) ;
fprintf(file, ", ") ;
type->generate_type_name(file, type_generate_header) ;
fprintf(file, ">") ;
if (generate_mode == type_generate_class_reference) fprintf(file, " *") ;
else fprintf(file, " ") ;
}

void T_type_list::generate_type(FILE *file) const
{
fprintf(file, "new T_type_list(%d, ", mode_weak) ;
type->generate_type(file) ;
fprintf(file, ")") ;
}

// }{--------------------------------------------------------------

void T_type_reference::dump(FILE *file) const
{
if (type == NULL)
	{
	fprintf(file, "any reference") ;
	}
else
	{
	if (mode_weak) fprintf(file, "weak ") ;
	fprintf(file, "reference on") ;
	type->dump(file) ;
	}
}

const T_object_class_field *T_type_reference::search_field(T_entity_program *program, int direct_search, const T_string name) const
{
if (!direct_search) return FALSE ;
if (type == NULL) return program->class_class->search_field(program, name) ;
return type->search_field(program, FALSE, name) ;
}

int T_type_reference::is_good_for_call(T_entity_program *program, const T_type *call_type) const
{
if (call_type->is_null()) return TRUE ;
if (call_type->is_reference())
	{
/*
	type->dump(stderr) ;
	fprintf(stderr, " vs ") ;
	((T_type_reference *)call_type)->type->dump(stderr) ;
	fprintf(stderr, "\n") ;
*/
	return ((T_type_reference *)call_type)->type->is_sub_type(type) ;
	}
return FALSE ;
}

const T_object_class_method *T_type_reference::search_call(T_entity_program *program, int direct_search, const T_string name, T_expression_call_access *call) const
{
//fprintf(stderr, "type_reference : type=%d\n", type->get_class()) ;
if (!direct_search) return FALSE ;
if (type == NULL) return program->class_class->search_call(program, name, call) ;
return type->search_call(program, FALSE, name, call) ;
}

void T_type_reference::verify_semantic(T_entity_compiler *compiler, T_entity_program *program, T_scope *scope, T_type_method *method_type)
{
if (type != NULL) type->verify_semantic(compiler, program, scope, method_type) ;
}

void T_type_reference::generate_type_name(FILE *file, int generate_mode) const
{
switch (generate_mode)
	{
	case type_generate_value: generate_mode = type_generate_class_reference ; break ;
	case type_generate_instance: generate_mode = type_generate_instance_reference ; break ;
	case type_generate_introspect: generate_mode = type_generate_introspect_reference ; break ;
	}
if (type != NULL)
	{
	type->generate_type_name(file, generate_mode) ;
	}
else
	{
	switch (generate_mode)
		{
		case type_generate_value: fprintf(file, "CLASS_") ; break ;
		case type_generate_instance: fprintf(file, "INSTANCE_") ; break ;
		case type_generate_introspect: fprintf(file, "INTROSPECT_") ; break ;
		case type_generate_class_reference: fprintf(file, "CLASS_ *") ; break ;
		case type_generate_instance_reference: fprintf(file, "INSTANCE_ *") ; break ;
		case type_generate_introspect_reference: fprintf(file, "INTROSPECT_ *") ; break ;
		case type_generate_header_reference: fprintf(file, "HEADER_ *") ; break ;
		case type_generate_header: fprintf(file, "HEADER_") ; break ;
		case type_generate_introspect_object: fprintf(file, "introspect_") ; break ;
		}
	}
}

void T_type_reference::generate_type(FILE *file) const
{
fprintf(file, "new T_type_reference(%d, ", mode_weak) ;
if (type != NULL) type->generate_type(file) ;
else fprintf(file, "NULL") ;
fprintf(file, ")") ;
}

// }{--------------------------------------------------------------

void T_type_vector::dump(FILE *file) const
{
fprintf(file, "any vector") ;
}

const T_object_class_field *T_type_vector::search_field(T_entity_program *program, int direct_search, const T_string name) const
{
return program->vector_class->search_field(program, name) ;
}

void T_type_vector::generate_type_name(FILE *file, int generate_mode) const
{
fprintf(file, "CLASS_Vector") ;
}

// }{--------------------------------------------------------------

void T_type_type::dump(FILE *file) const
{
fprintf(file, "type") ;
}

const T_object_class_field *T_type_type::search_field(T_entity_program *program, int direct_search, const T_string name) const
{
return NULL ;
}

void T_type_type::generate_type_name(FILE *file, int generate_mode) const
{
fprintf(file, "CLASS_Type") ;
}

// }{--------------------------------------------------------------
/*
void T_type_vector_item::dump(FILE *file) const
{
fprintf(file, "vector item") ;
}
*/
// }{--------------------------------------------------------------

void T_type_method::dump_content(FILE *file) const
{
prototype.dump(file) ;
if (returned_type != NULL)
	{
	fprintf(file, "return ") ;
	returned_type->dump(file) ;
	}
}

void T_type_method::dump(FILE *file) const
{
fprintf(file, "method") ;
dump_content(file) ;
}

int T_type_method::parse(T_entity_compiler *compiler)
{
if (!prototype.parse(compiler)) return FALSE ;
if (compiler->parse_keyword("returns"))
	{
	returned_type = compiler->parse_type() ;
	if (returned_type == NULL)
		{
		compiler->add_error(compiler->location, "type expected.") ;
		return FALSE ;
		}
	}
else
	{
	returned_type = new T_type_void(location) ;
	}
return TRUE ;
}

int T_type_method::can_overload(const T_type_method *ref)
{
return prototype.can_overload(&ref->prototype) ;
}

int T_type_method::is_good_for_call(T_entity_program *program, T_expression_constructor_arguments *call) const
{
return prototype.is_good_for_call(program, call) ;
}

void T_type_method::verify_semantic(T_entity_compiler *compiler, T_entity_program *program, T_scope *scope, T_type_method *method_type, T_object_class *object_class)
{
prototype.verify_semantic(compiler, program, scope, NULL, object_class) ;
if (returned_type != NULL) returned_type->verify_semantic(compiler, program, scope, method_type) ;
}

// }{--------------------------------------------------------------
